Defunds Crypt [web]

Defunds Crypt

One year since defund's descent. One crypt. One void to fill. Clam must do it, and so must you.

Recon

The website shows an image and an option to upload an image file. The source code of the website contains some helpful information.

<!-- Defund was a big fan of open source so head over to /src.php -->
<!-- Also I have a flag in the filesystem at /flag.txt but too bad you can't read it -->

The src.php contains the source code of the website, including the php code which handles the upload.

<?php
    if ($_SERVER["REQUEST_METHOD"] === "POST") {
        // I just copy pasted this from the PHP site then modified it a bit
        // I'm not gonna put myself through the hell of learning PHP to write one lousy angstrom chall
        try {
            if (
                !isset($_FILES['imgfile']['error']) ||
                is_array($_FILES['imgfile']['error'])
            ) {
                throw new RuntimeException('The crypt rejects you.');
            }
            switch ($_FILES['imgfile']['error']) {
                case UPLOAD_ERR_OK:
                    break;
                case UPLOAD_ERR_NO_FILE:
                    throw new RuntimeException('You must leave behind a memory lest you be forgotten forever.');
                case UPLOAD_ERR_INI_SIZE:
                case UPLOAD_ERR_FORM_SIZE:
                    throw new RuntimeException('People can only remember so much.');
                default:
                    throw new RuntimeException('The crypt rejects you.');
            }
            if ($_FILES['imgfile']['size'] > 1000000) {
                throw new RuntimeException('People can only remember so much..');
            }
            $finfo = new finfo(FILEINFO_MIME_TYPE);
            if (false === $ext = array_search(
                $finfo->file($_FILES['imgfile']['tmp_name']),
                array(
                    '.jpg' => 'image/jpeg',
                    '.png' => 'image/png',
                    '.bmp' => 'image/bmp',
                ),
                true
            )) {
                throw new RuntimeException("Your memory isn't picturesque enough to be remembered.");
            }
            if (strpos($_FILES["imgfile"]["name"], $ext) === false) {
                throw new RuntimeException("The name of your memory doesn't seem to match its content.");
            }
            $bname = basename($_FILES["imgfile"]["name"]);
            $fname = sprintf("%s%s", sha1_file($_FILES["imgfile"]["tmp_name"]), substr($bname, strpos($bname, ".")));
            if (!move_uploaded_file(
                $_FILES['imgfile']['tmp_name'],
                "./memories/" . $fname
            )) {
                throw new RuntimeException('Your memory failed to be remembered.');
            }
            http_response_code(301);
            header("Location: /memories/" . $fname);
        } catch (RuntimeException $e) {
            echo "<p>" . $e->getMessage() . "</p>";
        }
    }
?>

Solution

The source code checks the mime-type of the uploaded file against an array of acceptable image files and then search if the associated extension is part of the filename with the strpos function, so if we upload a file named image.jpg.php it is accepted.

We downloaded the crypt.jpg image and added <?php include '/flag.txt'; ?> to the end of the file and reuploaded this as crypt.jpg.php. At the end of the returned blob we will see the flag.

Flag

actf{th3_ch4ll3ng3_h4s_f4ll3n_but_th3_crypt_rem4ins}